using System;
using System.Collections.Generic;
using System.Text;

using Weazel.Microblaze.Simulator.Engine;

namespace Weazel.Microblaze.Simulator.Components.Microblaze.Instructions
{
    /// <summary>
    /// Integer Compare
    /// </summary>
    public class Cmp : Instruction
    {
        private enum CmpInstructionKind
        {
            None,   // error
            cmp,    // Compare rB with rA (signed)
            cmpu    // Compare rB with rA (unsigned)
        }

        public Cmp()
            : base("cmp")
        {
        }

        private CmpInstructionKind kind;

        public override int CycleCount
        {
            get { return 1; }
        }

        public override void Set(uint address, uint value)
        {
            base.Set(address, value);

            uint sub_opcode = value & 0x3;

            kind =
              sub_opcode == 0x03 ? CmpInstructionKind.cmpu :
              sub_opcode == 0x01 ? CmpInstructionKind.cmp :
              CmpInstructionKind.None;

            if (kind == CmpInstructionKind.None)
                throw new UnknownInstructionException(value);
        }

        /// <summary>
        /// The contents of register rA is subtracted from the contents of register rB and 
        /// the result is placed into register rD. 
        /// The MSB bit of rD is adjusted to shown true relation between rA and rB. If the 
        /// U bit is set, rA and rB is considered unsigned values. If the U bit is clear, 
        /// rA and rB is considered signed values.
        /// </summary>    
        public override void Execute(MicroblazeComponent MicroblazeComponent)
        {
            uint ra = MicroblazeComponent.ReadRegister(SourceRegisterA);
            uint rb = MicroblazeComponent.ReadRegister(SourceRegisterB);

            uint rd = rb + ~ra + 1;

            if (kind == CmpInstructionKind.cmpu)
            {
                if (ra > rb)
                {
                    rd |= 0x70000000;
                }
                else
                {
                    rd &= 0x7FFFFFFF;
                }
            }
            else
            {
                if ((int)ra > (int)rb)
                {
                    rd |= 0x70000000;
                }
                else
                {
                    rd &= 0x7FFFFFFF;
                }
            }

            MicroblazeComponent.WriteRegister(DestinationRegister, rd, Address);
        }

        public override string ToString()
        {
            return string.Format("{0} r{1}, r{2}, r{3}",
              kind,
              DestinationRegister,
              SourceRegisterA,
              SourceRegisterB
            );
        }
    }
}
